/*
'*' - CLASS/MODULE
''  - parametros da entidade 
'!' - parametros extra nao persistidos (qualquer tipo)
'#' - metodos extra da entidade
'@' - metodos do controller
*/

#include "myfile.h"
#include "properties.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Property **readProperties(char *fname, int *numPropertiesRead)
{
  int propertiesSize=0;
  Property **properties = 0;
  int numProperties=0;
  Property *p;

  while(p=getProperty(fname,numProperties))
  {
    if(numProperties == propertiesSize)
    {
      Property **newProperties = (Property **)realloc(properties,sizeof(Property *)*(propertiesSize+16));
      if(newProperties)
      {
        properties = newProperties;
        propertiesSize += 16;
      }
    }
    properties[numProperties]=p;
    ++numProperties;
  }

  if(numPropertiesRead) *numPropertiesRead = numProperties;
  return properties;
}

char *findProperty(char *name, Property **properties, int numProperties)
{
  int i;

  for(i=0; i<numProperties; i++)
  {
    if(!strcmp(properties[i]->name,name)) return properties[i]->value;
  }

  return 0;
}

char *createFileName(char *module, char *class, char *extension)
{
  char *fname = 0;

  if(module) {
    int len = strlen(module) + strlen(class) + strlen(extension) + 3;
    fname = (char *)malloc(sizeof(char)*len);
    if(fname) sprintf(fname,"%s_%s%s",module,class,extension);
  } else {
    int len = strlen(class) + strlen(extension) + 2;
    fname = (char *)malloc(sizeof(char)*len);
    if(fname) sprintf(fname,"%s%s",class,extension);
  }

  return fname;
}

typedef struct
{
  char *key;
  char *cType;
  char *gvarType;
  char *bdType;
} TypesMap;

TypesMap typesMap[] = {
  {"CHAR", "char", "CHAR", "CHAR"},
  {"INT16", "int16_t", "INT16", "INTEGER"},
  {"INT32", "int32_t", "INT32", "INTEGER"},
  {"INT64", "int64_t", "INT64", "INTEGER"},
  {"REAL", "double", "DOUBLE", "DOUBLE"},
  {"ID", "int64_t", "INT64", "INTEGER"},
  {"PCHAR", "char*", "PCHAR", "TEXT"},
  {0, 0, 0, 0}
};

TypesMap *findMap(TypesMap *map, char *key)
{
  int i=0;

  while(map[i].key)
  {
    if(!strcmp(key,map[i].key)) return &map[i];
    ++i;
  }

  return 0;
}

char *typeToC(char *type)
{
  TypesMap *map = findMap(typesMap,type);

  if(!map) return 0;
  return map->cType;
}

char *typeToGVar(char *type)
{
  TypesMap *map = findMap(typesMap,type);

  if(!map) return 0;
  return map->gvarType;
}

char *typeToBDVar(char *type)
{
  TypesMap *map = findMap(typesMap,type);

  if(!map) return 0;
  return map->bdType;
}


void printGetFunctionDecl(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"static %s get_%s(%s_%s *this)",ctype,name,modulo,classe);
  else printFile(file,"static %s get_%s(%s *this)",ctype,name,classe);
}


void printGetEntityFunctionDecl(MYFile file, char *modulo, char *classe, char *name)
{
  char *typeAndName = &name[3];
  if(modulo) printFile(file,"static %s* get_%s(%s_%s *this)",typeAndName,typeAndName,modulo,classe);
  else printFile(file,"static %s* get_%s(%s *this)",typeAndName,typeAndName,classe);
}

void printSetFunctionDecl(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"static void set_%s(%s_%s *this, %s novo)",name,modulo,classe,ctype);
  else printFile(file,"static void set_%s(%s *this, %s novo)",name,classe,ctype);
}

void printSetEntityFunctionDecl(MYFile file, char *modulo, char *classe, char *name)
{
  char *typeAndName = &name[3];
  if(modulo) printFile(file,"static void set_%s(%s_%s *this, %s *novo)",typeAndName,modulo,classe,typeAndName);
  else printFile(file,"static void set_%s(%s *this, %s *novo)",typeAndName,classe,typeAndName);
}

void printExtraFunctionDecl(MYFile file, char *modulo, char *classe, char *name, char *retAndArgs)
{
  char *ret = retAndArgs;
  char *args;

  args = strstr(retAndArgs,",");
  if(args)
  {
    args[0] = 0; ++args;
    if(modulo) printFile(file,"static %s %s(%s_%s *this, %s)",ret,name,modulo,classe,args);
    else printFile(file,"static %s %s(%s *this, %s)",ret,name,classe,args);  
    args--; args[0] = ',';
  }
  else
  {
    if(modulo) printFile(file,"static %s %s(%s_%s *this)",ret,name,modulo,classe);
    printFile(file,"static %s %s(%s *this)",ret,name,classe);
  }
}

void printInterfaceGetField(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"  %s (*get_%s)(%s_%s *this);\n",ctype,name,modulo,classe);
  else printFile(file,"  %s (*get_%s)(%s *this);\n",ctype,name,classe);
}

void printInterfaceSetField(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"  void (*set_%s)(%s_%s *this, %s novo);\n",name,modulo,classe,ctype);
  else printFile(file,"  void (*set_%s)(%s *this, %s novo);\n",name,classe,ctype);
}

void printExtraFunctionField(MYFile file, char *modulo, char *classe, char *name, char *retAndArgs)
{
  char *ret = retAndArgs;
  char *args;

  args = strstr(retAndArgs,",");
  if(args)
  {
    args[0] = 0; ++args;
    if(modulo) printFile(file,"  %s (*%s)(%s_%s *this, %s);\n",ret,name,modulo,classe,args);
    else printFile(file,"  %s (*%s)(%s *this, %s);\n",ret,name,classe,args);  
    args--; args[0] = ',';
  }
  else
  {
    if(modulo) printFile(file,"  %s (*%s)(%s_%s *this);\n",ret,name,modulo,classe);
    else printFile(file,"  %s (*%s)(%s *this);\n",ret,name,classe);
  }
}

void printGetFunctionImpl(MYFile file, char *name, char *gvarType)
{
  if(gvarType)
  {
    printFile(file,"\n{\n  return this->%s.%sVAL;\n}\n\n",name,gvarType);
  }
  else
  {
    printFile(file,"\n{\n  return this->%s;\n}\n\n",name);
  }
}

void printSetFunctionImpl(MYFile file, char *name, char *gvarType)
{
  if(strstr(name,"id_")==name)
  {
    char *nameAndType = &name[3];
    if(gvarType)
      printFile(file,"\n{\n  setVar(&this->%s,novo);\n  this->%sPtr = 0;\n}\n\n",name,nameAndType);
    else
      printFile(file,"\n{\n  this->%s = novo;\n  this->%sPtr = 0;\n}\n\n",name,nameAndType);
  }
  else
  {
    if(gvarType)
      printFile(file,"\n{\n  setVar(&this->%s,novo);\n}\n\n",name);
    else
      printFile(file,"\n{\n  this->%s = novo;\n}\n\n",name);
  }
}

void printGetEntityFunctionImpl(MYFile file, char *modulo, char *classe, char *name)
{
  char *nameAndType = &name[3];

  printFile(file,"\n{\n  if(!this->%sPtr) this->%sPtr=loadEntity(%s,getVarSTR(&this->%s));\n  return this->%sPtr;\n}\n\n",
            nameAndType,nameAndType,nameAndType,name,nameAndType);
}

void printSetEntityFunctionImpl(MYFile file, char *modulo, char *classe, char *name)
{
  char *idType = typeToGVar("ID");
  char *nameAndType = &name[3];

  printFile(file,"\n{\n  this->%sPtr = novo;\n  this->%s.%sVAL = novo->id.%sVAL;\n}\n\n",nameAndType,name,idType,idType);
}

void printControllerFunctionDecl(MYFile file, char *modulo, char *classe, char *name, char *retAndArgs)
{
  char *ret = retAndArgs;
  char *args;

  args = strstr(retAndArgs,",");
  if(args)
  {
    args[0] = 0; ++args;
    if(modulo) printFile(file,"%s %s_%s_%s(%s)",ret,modulo,classe,name,args);
    else printFile(file,"%s %s_%s(%s)",ret,classe,name,args);  
    args--; args[0] = ',';
  }
  else
  {
    if(modulo) printFile(file,"%s %s_%s_%s()",ret,modulo,classe,name);
    else printFile(file,"%s %s_%s()",ret,classe,name);
  }
}


void createH(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,".h");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//ifdef/define 
  if(modulo)
  {
    printFile(file,"#ifndef %s_%s_h\n",modulo,classe);
    printFile(file,"#define %s_%s_h\n\n",modulo,classe);
  }
  else
  {
    printFile(file,"#ifndef %s_h\n",classe);
    printFile(file,"#define %s_h\n\n",classe);
  }

//includes
  printFile(file,"#include <stdint.h>\n");
  printFile(file,"#include \"genericv.h\"\n");
  printFile(file,"#include \"stub.h\"\n");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];

    if(p->name[0]=='!') continue;
    if(p->name[0]=='*') continue;
    if(p->name[0]=='#') continue;
    if(p->name[0]=='@') continue;

    char *ctype = typeToC(p->value);

    if(!ctype)
    {
      int len=strlen(p->value);
      while( (len>0) && ( (p->value[len-1]=='*') || (isspace(p->value[len-1])) ) ) --len;
      if(len)
      {
        char bkp = p->value[len];
        p->value[len]=0;
        printFile(file,"#include \"%s.h\" //%s\n",p->value,p->name);
        p->value[len]=bkp;
      }
    }

    if(strstr(p->name,"id_")==p->name)
      printFile(file,"#include \"%s.h\" //%s\n",p->name+3,p->name);
  }

//definicao da estrutura (atributos)
  printFile(file,"\n\ntypedef struct {\n");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='#') continue;
    if(p->name[0]=='@') continue;

    char *name = p->name;
    char *ctype;

    if(name[0]=='!') //parametros extra comecam com !, e podem ser de qq tipo
    {
      name++;
      ctype=p->value;
      printFile(file,"  %s %s;\n",ctype,name);
    }
    else
    {
      ctype = typeToC(p->value);
      if(!ctype) continue;
      printFile(file,"  GenericVar %s;\n",name);

      if(strstr(name,"id_")==name)
      {
        char *typeAndName = &name[3];
        printFile(file,"  %s* %sPtr;\n",typeAndName,typeAndName);
      }
    }
  }
  printFile(file,"} ");
  if(modulo) printFile(file,"%s_",modulo);
  printFile(file,"%s;\n\n",classe);

//definicao da interface (metodos)
  printFile(file,"\n\ntypedef struct {\n");

  if(modulo) printFile(file,"  %s_%s* (*create)();\n",modulo,classe);
  else printFile(file,"  %s* (*create)();\n",classe);
  printFile(file,"  FieldEntry* (*getFields)();\n");

  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#') printExtraFunctionField(file,modulo,classe,p->name+1,p->value);
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!') //parametros extra comecam com !, e podem ser de qq tipo
      {
        name++;
        ctype=p->value;
        printInterfaceGetField(file,modulo,classe,ctype,name);
        printInterfaceSetField(file,modulo,classe,ctype,name);
      }
      else
      {
        ctype = typeToC(p->value);
        if(!ctype) continue;

        printInterfaceGetField(file,modulo,classe,ctype,name);
        printInterfaceSetField(file,modulo,classe,ctype,name);
        if(strstr(name,"id_")==name)
        {
          char *n = &name[3];
          char type[1024];

          int len = strlen(n);
          memcpy(type,n,len);
          type[len]='*';
          type[len+1]=0;

          printInterfaceGetField(file,modulo,classe,type,n);
          printInterfaceSetField(file,modulo,classe,type,n);
        }
      }


    }
  }
  printFile(file,"} ");

//declaracao da interface
  if(modulo)
  {
    printFile(file,"%s_%sInterface;\n\n",modulo,classe);
    printFile(file,"extern %s_%sInterface %s_%sI;\n\n",modulo,classe,modulo,classe);
  }
  else
  {
    printFile(file,"%sInterface;\n\n",classe);
    printFile(file,"extern %sInterface %sI;\n\n",classe,classe);
  }

  printFile(file,"#endif\n");

  closeFile(file);
}

void createC(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,".c");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//includes
  printFile(file,"#include <stdlib.h>\n");
  printFile(file,"#include <string.h>\n");
  printFile(file,"#include \"genericdao.h\"\n");
  if(modulo) printFile(file,"#include \"%s_%s.h\"\n\n",modulo,classe);
  else printFile(file,"#include \"%s.h\"\n\n",classe);

//declaracao dos metodos extra, get/set, e get/set dos parametros extra
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#')
    {
      printExtraFunctionDecl(file,modulo,classe,p->name+1,p->value);
      printFile(file,";\n");
    }
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!') //parametros extra comecam com !, e podem ser de qq tipo
      {
        name++;
        ctype=p->value;

        printGetFunctionDecl(file,modulo,classe,ctype,name);
        printFile(file,";\n");
        printSetFunctionDecl(file,modulo,classe,ctype,name);
        printFile(file,";\n");
      }
      else
      {
        ctype = typeToC(p->value);
        if(!ctype) continue;

        printGetFunctionDecl(file,modulo,classe,ctype,name);
        printFile(file,";\n");
        printSetFunctionDecl(file,modulo,classe,ctype,name);
        printFile(file,";\n");

        if(strstr(name,"id_")==name)
        {
          printGetEntityFunctionDecl(file,modulo,classe,name);
          printFile(file,";\n");
          printSetEntityFunctionDecl(file,modulo,classe,name);
          printFile(file,";\n");
        }
      }
    }
  }
  printFile(file,"\n");

//implementacao do create()  
  if(modulo) printFile(file,"static %s_%s* create()\n{\n  %s_%s *ptr = malloc(sizeof(%s_%s));\n  if(!ptr) return 0;\n  memset(ptr,0,sizeof(%s_%s));\n",modulo,classe,modulo,classe,modulo,classe,modulo,classe);
  else printFile(file,"static %s* create()\n{\n  %s *ptr = malloc(sizeof(%s));\n  if(!ptr) return 0;\n  memset(ptr,0,sizeof(%s));\n",classe,classe,classe,classe);
  for(i=0; i<numProperties; i++)  
  {
    Property *p = properties[i];
    if(p->name[0]=='!') continue;
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;
    if(p->name[0]=='#') continue;
       
    char *gvarType = typeToGVar(p->value);
    if(!gvarType) continue;
  
    printFile(file,"  ptr->%s=(GenericVar)GVAR(T_%s);\n",p->name,gvarType);
  }
  printFile(file,"  return ptr;\n}\n\n");

//implementacao dos metodos
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#')
    {
      printExtraFunctionDecl(file,modulo,classe,p->name+1,p->value);
      printFile(file,"\n{\n\n}\n\n");
    }
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!') //parametros extra comecam com !, e podem ser de qq tipo
      {
        name++;
        ctype=p->value;
        printGetFunctionDecl(file,modulo,classe,ctype,name);
        printGetFunctionImpl(file,name,0);

        printSetFunctionDecl(file,modulo,classe,ctype,name);
        printSetFunctionImpl(file,name,0);
      }
      else
      {
        ctype = typeToC(p->value);
        if(!ctype) continue;

        char *gvarType = typeToGVar(p->value);

        printGetFunctionDecl(file,modulo,classe,ctype,name);
        printGetFunctionImpl(file,name,gvarType);

        printSetFunctionDecl(file,modulo,classe,ctype,name);
        printSetFunctionImpl(file,name,gvarType);

        if(strstr(name,"id_")==name)
        {
          printGetEntityFunctionDecl(file, modulo, classe, name);
          printGetEntityFunctionImpl(file, modulo, classe, name);
          printSetEntityFunctionDecl(file, modulo, classe, name);
          printSetEntityFunctionImpl(file, modulo, classe, name);
        }
      }
    }
  }
  printFile(file,"\n");

//implementacao do metodo com nome e endereco (relativo) de cada parametro
  printFile(file,"static FieldEntry *getFields()\n{\n");
  if(modulo) printFile(file,"  static %s_%s v;\n",modulo,classe);
  else printFile(file,"  static %s v;\n",classe);
  printFile(file,"  static FieldEntry fe[] = {\n");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];

    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;
    if(p->name[0]=='#') continue;
    if(p->name[0]=='!') continue;

    printFile(file,"    {\"%s\", ((long)&v.%s - (long)&v)},\n",p->name,p->name);
  }
  printFile(file,"    {0, 0}\n");
  printFile(file,"  };\n  return fe;\n}\n\n");

//inicializacao da interface
  if(modulo)
  {
    printFile(file,"%s_%sInterface %s_%sI = {\n",modulo,classe,modulo,classe);
  }
  else
  {
    printFile(file,"%sInterface %sI = {\n",classe,classe);
  }
  printFile(file,"create,\ngetFields");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#')
    {
      printFile(file,",\n%s",p->name+1);
    }
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!') //parametros extra comecam com !, e podem ser de qq tipo
      {
        name++;
        ctype=p->value;
        printFile(file,",\nget_%s",name);
        printFile(file,",\nset_%s",name);
      }
      else
      {
        ctype = typeToC(p->value);
        if(!ctype) continue;
        printFile(file,",\nget_%s",name);
        printFile(file,",\nset_%s",name);
        if(strstr(name,"id_")==name)
        {
          printFile(file,",\nget_%s",name+3);
          printFile(file,",\nset_%s",name+3);
        }
      }
    }
  }
  printFile(file,"\n};\n");

  closeFile(file);
}

void createControllerH(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,"_controller.h");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//ifdef/define
  if(modulo)
  {
    printFile(file,"#ifndef %s_%s_controller_h\n",modulo,classe);
    printFile(file,"#define %s_%s_controller_h\n\n",modulo,classe);
    printFile(file,"#include \"%s_%s.h\"\n\n",modulo,classe);
  }
  else
  {
    printFile(file,"#ifndef %s_controller_h\n",classe);
    printFile(file,"#define %s_controller_h\n\n",classe);
    printFile(file,"#include \"%s.h\"\n\n",classe);
  }

//declaracao dos metodos do controller
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]!='@') continue;

    printControllerFunctionDecl(file, modulo, classe, p->name+1, p->value);
    printFile(file,";\n\n");
  }

  printFile(file,"#endif\n");

  closeFile(file);
}

void createControllerC(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,"_controller.c");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//includes
  if(modulo) printFile(file,"#include \"%s_%s_controller.h\"\n\n",modulo,classe);
  else printFile(file,"#include \"%s_controller.h\"\n\n",classe);

//implementacao dos metodos do controller
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]!='@') continue;

    printControllerFunctionDecl(file, modulo, classe, p->name+1, p->value);
    printFile(file,"\n{\n\n}\n\n");
  }

//exportando metodos do controller
  int n=0;
  printFile(file,"DECL(\n");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]!='@') continue;

    if(n) printFile(file,",\n");
    ++n;
    printFile(file,"  M(%s)",p->name+1);
  }
  printFile(file,"\n)\n");

  closeFile(file);
}

void main(int argc, char *argv[])
{
  int i;
  int numProperties;

  Property **properties = readProperties(argv[1],&numProperties);

  createH(properties,numProperties);
  createC(properties,numProperties);

  createControllerH(properties,numProperties);
  createControllerC(properties,numProperties);

  for(i=0; i<numProperties; i++) delProperty(properties[i]);
  free(properties);
}
